<?php

namespace app\common\service;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class ApiClientService
{

    protected $appId;
    protected $appSecret;
    protected $baseUrl;
    protected $httpClient;

    /**
     * 构造函数
     * @param string $appId 应用ID
     * @param string $appSecret 应用密钥
     * @param string $baseUrl API基础URL
     */
    public function __construct($appId, $appSecret, $baseUrl)
    {
        $this->appId = $appId;
        $this->appSecret = $appSecret;
        $this->baseUrl = rtrim($baseUrl ?: '', '/');
        $this->httpClient = new Client([
            'base_uri' => $this->baseUrl,
            'timeout' => 30,
        ]);
    }

    /**
     * 发起GET请求
     * @param string $path 请求路径
     * @param array $query Query参数
     * @return array
     */
    public function get($path, $query = [])
    {
        return $this->request('GET', $path, $query);
    }

    /**
     * 发起POST请求
     * @param string $path 请求路径
     * @param array $data 请求数据
     * @param array $query Query参数
     * @return array
     */
    public function post($path, $data = [], $query = [])
    {
        return $this->request('POST', $path, $query, $data);
    }

    /**
     * 发起PUT请求
     * @param string $path 请求路径
     * @param array $data 请求数据
     * @param array $query Query参数
     * @return array
     */
    public function put($path, $data = [], $query = [])
    {
        return $this->request('PUT', $path, $query, $data);
    }

    /**
     * 发起DELETE请求
     * @param string $path 请求路径
     * @param array $query Query参数
     * @return array
     */
    public function delete($path, $query = [])
    {
        return $this->request('DELETE', $path, $query);
    }

    /**
     * 发起API请求
     * @param string $method 请求方法
     * @param string $path 请求路径
     * @param array $query Query参数
     * @param array $body 请求体数据
     * @return array
     */
    public function request($method, $path, $query = [], $body = [])
    {
        $timestamp = time();
        $nonce = $this->generateNonce();
        $method = strtoupper($method);

        // 1. 准备待签名字符串
        ksort($query);
        $sortedQuery = http_build_query($query);

        $bodyString = $body;

        // 移除路径开头的斜杠（如果有）
        $path = ltrim($path, '/');

        $stringToSign = "{$method}\n{$path}\n{$sortedQuery}\n{$timestamp}\n{$nonce}\n{$bodyString}";
        // trace($stringToSign, '客户端签名的数据');

        // 2. 生成签名
        $signature = $this->generateSignature($stringToSign);

        // 3. 组装请求头
        $headers = [
            'X-App-ID' => $this->appId,
            'X-Timestamp' => $timestamp,
            'X-Nonce' => $nonce,
            'X-Signature' => $signature,
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ];

        // 4. 发送请求
        try {
            $options = [
                'headers' => $headers,
            ];

            if (!empty($query)) {
                $options['query'] = $query;
            }

            if (!empty($bodyString)) {
                $options['body'] = $bodyString;
            }

            $response = $this->httpClient->request($method, '/' . $path, $options);

            return json_decode($response->getBody()->getContents(), true);

        } catch (RequestException $e) {
            // 处理请求异常
            if ($e->hasResponse()) {
                $errorBody = $e->getResponse()->getBody()->getContents();
                return json_decode($errorBody, true) ?: [
                    'code' => $e->getResponse()->getStatusCode(),
                    'message' => $errorBody
                ];
            }
            return [
                'code' => 500,
                'message' => $e->getMessage()
            ];
        }
    }

    /**
     * 生成签名
     * @param string $stringToSign 待签名字符串
     * @return string
     */
    private function generateSignature($stringToSign)
    {
        $signature = hash_hmac('sha256', $stringToSign, $this->appSecret, true);
        return base64_encode($signature);
    }

    /**
     * 生成随机数
     * @return string
     */
    private function generateNonce()
    {
        return md5(uniqid(mt_rand(), true));
    }
}